package com.ruoyi.project.common;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ijpay.core.enums.SignType;
import com.ijpay.core.enums.TradeType;
import com.ijpay.core.kit.*;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.WxPayApiConfig;
import com.ijpay.wxpay.WxPayApiConfigKit;
import com.ijpay.wxpay.model.*;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.WxMiniProgramUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
import com.ruoyi.framework.config.RuoYiConfig;
import com.ruoyi.framework.config.ServerConfig;
import com.ruoyi.framework.config.WxAccessTokenManager;
import com.ruoyi.framework.security.service.TokenService;
import com.ruoyi.framework.web.controller.BaseController;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysWechatPay;
import com.ruoyi.project.common.domain.Trade;
import com.ruoyi.project.system.mapper.SysWechatPayMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping("/wechat")
@Slf4j
public class WxMiniProgramController extends BaseController {


    public static final String GET_MINI_PROGRAM_QR_CODE_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";

    @Value("${wx.appId}")
    private String MINI_PROGRAM_APP_ID;

    @Value("${wx.secret}")
    private String MINI_PROGRAM_APP_SECRET;

    @Value("${wx.apiKey}")
    private String apiKey;

    @Value("${wx.mchId}")
    private String mchId;

    @Value("${wx.certPath}")
    private String certPath;

    @Value("${alan.clientIp}")
    private String clientIp;


    @Autowired
    private WxMiniProgramUtils wxMiniProgramUtils;


    private void initConfig() {
        WxPayApiConfig apiConfig;
        apiConfig = WxPayApiConfig.builder()
                .appId(MINI_PROGRAM_APP_ID)
                .mchId(mchId)
                .apiKey(apiKey)
                .partnerKey(apiKey)
                .certPath(certPath)
                .build();
        WxPayApiConfigKit.putApiConfig(apiConfig);
    }


    /**
     * 微信小程序支付
     */
    @RequestMapping(value = "/pay", method = {RequestMethod.GET})
    @ResponseBody
    public AjaxResult mini(Trade trade) {
        initConfig();
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        Map<String, String> params = UnifiedOrderModel
                .builder()
                .appid(wxPayApiConfig.getAppId())
                .mch_id(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .body(trade.getBody())
                .out_trade_no(WxPayKit.generateStr())
                .total_fee(trade.getTotalAmount())
                .spbill_create_ip(clientIp)
                .notify_url("https://pawssomcoupons.com.cn/")
                .trade_type(TradeType.JSAPI.getTradeType())
                .openid(trade.getOpenId())
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        String xmlResult = WxPayApi.pushOrder(false, params);
        log.info(xmlResult);
        Map<String, String> result = WxPayKit.xmlToMap(xmlResult);
        String returnCode = result.get("return_code");
        String returnMsg = result.get("return_msg");
        if (!WxPayKit.codeIsOk(returnCode)) {
            return AjaxResult.error(returnMsg);
        }
        String resultCode = result.get("result_code");
        if (!WxPayKit.codeIsOk(resultCode)) {
            return AjaxResult.error(returnMsg);
        }
        // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
        String prepayId = result.get("prepay_id");
        Map<String, String> packageParams = WxPayKit.miniAppPrepayIdCreateSign(wxPayApiConfig.getAppId(), prepayId,
                wxPayApiConfig.getPartnerKey(), SignType.HMACSHA256);
        String jsonStr = JSON.toJSONString(packageParams);
        log.info("小程序支付的参数:" + jsonStr);
        return AjaxResult.success("200", jsonStr);
    }


    /**
     * 获取微信手机号
     *
     * @param encryptedData
     * @param code
     * @param iv
     * @return
     */
    @GetMapping("/getWxUserPhone")
    @Anonymous
    @ResponseBody
    public AjaxResult getWxUserPhone(String encryptedData, String code, String iv) {
        Map<String, String> map = new HashMap<>(3);
        JSONObject json = wxMiniProgramUtils.getEncryptPhone(code);
        String sessionKey = json.getString("session_key");
        String unionId = json.getString("unionid");
        String openId = json.getString("openid");
        map.put("sessionKey", sessionKey);
        map.put("unionId", unionId);
        map.put("openId", openId);
        return AjaxResult.success(wxMiniProgramUtils.decryptPhone(encryptedData, sessionKey, iv));
    }

    /**
     * 获取openId
     * @param code
     * @return
     */
    @GetMapping("/getOpenId")
    @Anonymous
    @ResponseBody
    public AjaxResult getOpenId(String code) {
        Map<String, String> map = new HashMap<>(3);
        JSONObject json = wxMiniProgramUtils.getEncryptPhone(code);
        String sessionKey = json.getString("session_key");
        String unionId = json.getString("unionid");
        String openId = json.getString("openid");
        map.put("sessionKey", sessionKey);
        map.put("unionId", unionId);
        map.put("openId", openId);
        return AjaxResult.success(map);
    }

    /**
     * 微信转账
     *
     * @param request
     * @param openId
     * @return
     */
    @RequestMapping(value = "/transfer", method = {RequestMethod.POST, RequestMethod.GET})
    @ResponseBody
    public String transfer(HttpServletRequest request, @RequestParam("openId") String openId) {
        initConfig();
        String ip = IpKit.getRealIp(request);
        if (StringUtils.isBlank(ip)) {
            ip = "127.0.0.1";
        }
        WxPayApiConfig wxPayApiConfig = WxPayApiConfigKit.getWxPayApiConfig();
        Map<String, String> params = TransferModel.builder()
                .mch_appid(wxPayApiConfig.getAppId())
                .mchid(wxPayApiConfig.getMchId())
                .nonce_str(WxPayKit.generateStr())
                .partner_trade_no(WxPayKit.generateStr())
                .openid(openId)
                .check_name("NO_CHECK")
                .amount("100")
                .desc("提现")
                .spbill_create_ip(ip)
                .build()
                .createSign(wxPayApiConfig.getPartnerKey(), SignType.MD5, false);

        // 提现
        String transfers = WxPayApi.transfers(params, wxPayApiConfig.getCertPath(), wxPayApiConfig.getMchId());
        log.info("提现结果:" + transfers);
        Map<String, String> map = WxPayKit.xmlToMap(transfers);
        String returnCode = map.get("return_code");
        String resultCode = map.get("result_code");
        if (WxPayKit.codeIsOk(returnCode) && WxPayKit.codeIsOk(resultCode)) {
            // 提现成功
        } else {
            // 提现失败
        }
        return transfers;
    }

    @Autowired
    private ServerConfig serverConfig;

    @GetMapping(value = "/generateMiniProgramQrCode")
    @Anonymous
    @ResponseBody
    public AjaxResult generateMiniProgramQrCodeToLocal(String userId, String couponId) {
        byte[] bytes = this.generateMiniProgramQrCode("pages/mine/coupon/scan", userId + "u" + couponId, "release");
        try {
            String uploadPath = RuoYiConfig.getUploadPath();
            String temp = "/profile/upload/";
            String uuid = IdUtils.simpleUUID();
            String time = DateUtil.format(new Date(), "yyyy/MM/dd");
            String domain = serverConfig.getUrl();
            String fileName = uuid + ".jpg";
            String url = domain + temp + time + "/" + fileName;
            BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
            if (!new File(uploadPath + "/" + time).exists()) {
                FileUtil.mkdir(uploadPath + "/" + time);
            }
            ImageIO.write(image, "jpg", new File(uploadPath + "/" + time + "/" + fileName));
            return AjaxResult.success("200", url);
        } catch (Exception e) {
            System.out.println(e);
            throw new RuntimeException("图片生成失败！");
        }
    }


    @Autowired
    private WxAccessTokenManager wxAccessTokenManager;

    /**
     * 生成小程序码
     *
     * @param jumpUrl
     * @param scene
     * @param envVersion
     * @return
     */
    public byte[] generateMiniProgramQrCode(String jumpUrl, String scene, String envVersion) {
        wxAccessTokenManager.refreshToken();
        String accessToken = wxAccessTokenManager.getAccessToken();
        ByteArrayOutputStream bos = null;
        try {
            //调用微信接口生成二维码
            String qrCodeUrl = String.format(GET_MINI_PROGRAM_QR_CODE_URL, accessToken);
            URL url = new URL(qrCodeUrl);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());

            // 包装请求参数
            // 可参考微信小程序接口文档地址 https://developers.weixin.qq.com/minigame/dev/api-backend/open-api/qr-code/wxacode.getUnlimited.html
            JSONObject paramJson = new JSONObject();
            paramJson.put("scene", scene);
            paramJson.put("page", jumpUrl);
            paramJson.put("width", 200);
            paramJson.put("is_hyaline", false);
            paramJson.put("auto_color", false);
            paramJson.put("check_path", false);
            paramJson.put("env_version", envVersion);
            printWriter.write(paramJson.toString());

            printWriter.flush();
            BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());
            bos = new ByteArrayOutputStream();
            int len;
            byte[] arr = new byte[8192];

            StringBuilder result = new StringBuilder();
            while ((len = bis.read(arr)) != -1) {
                bos.write(arr, 0, len);
                result.append(new String(arr));
                bos.flush();
            }
            bis.close();
            return bos.toByteArray();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }
}
